package com.clp.protocol.core.async;

import com.clp.protocol.core.utils.AssertUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

public class AnyIsSuccessPolicy implements SuccessPolicy {
    public static final AnyIsSuccessPolicy INSTANCE = new AnyIsSuccessPolicy();

    private AnyIsSuccessPolicy() {}

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public <P extends IPromise, F extends IFuture> void applyIPromise(P promise, List<F> futures) {
        int futuresSize = futures.size();

        if (futuresSize == 0) throw new IllegalArgumentException();
        if (promise.isDone()) return;

        final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
        for (int i = 0; i < isFinisheds.length; i++) {
            isFinisheds[i] = new AtomicBoolean(false);
        }
        final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
        for (int i = 0; i < isFaileds.length; i++) {
            isFaileds[i] = new AtomicBoolean(false);
        }

        Object lock = new Object();

        Class futureListenerClass = futures.get(0).getListenerClass();
        for (int index = 0; index < futuresSize; index++) {
            final int i = index;
            IFutureListener listener = (IFutureListener) Proxy.newProxyInstance(futures.get(0).getClass().getClassLoader(), new Class[]{futureListenerClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getName().equals(IFutureListener.getOperationCompleteMethod().getName())) {
                        synchronized (lock) {
                            if (futures.get(i).isSuccess()) {
                                isFaileds[i].set(false);
                                if (!promise.isDone()) promise.setSuccess();
                            } else {
                                isFaileds[i].set(true);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查是否全部执行失败
                                boolean isAllFinishedFailed = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (!isFaileds[j].get()) {
                                            isAllFinishedFailed = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                if (isLastFinished && isAllFinishedFailed) {
                                    promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                }
                            }
                            isFinisheds[i].set(true);
                        }
                        return null;
                    } else {
                        return method.invoke(args);
                    }
                }
            });
            futures.get(i).addListener(listener);
        }
    }

    @Override
    public CompletableFuture<Boolean> applyCompletableFuture(CompletableFuture<Boolean>... futures) {
        AssertUtil.notNull(futures, "futures");
        int futuresLength = futures.length;
        if (futuresLength == 0) throw new IllegalArgumentException();

        return CompletableFuture.allOf(futures).thenApplyAsync(new Function<Void, Boolean>() {
            @Override
            public Boolean apply(Void unused) {
                try {
                    for (CompletableFuture<Boolean> future : futures) {
                        if (future.get()) {
                            return true;
                        }
                    }
                    return false;
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    @Override
    public boolean applyBoolean(List<Boolean> isTrues) {
        for (boolean isTrue : isTrues) {
            if (isTrue) return true;
        }
        return false;
    }
}
